在.NET Core/Framework中使用依赖注入框架
综述
在传统的.NET Framework中通常会使用 new 关键词和静态对象或静态方法作为对象创建的形式,但是由于这两种方式存在以下缺陷:
使用New的方式创建对象的缺陷:
1、使用New的方式创建的对象,需要在由GC在不同的位置分别管理生命周期。
2、如果在不同的方法中需要创建相同的对象,往往需要用很多个new来进行管理。
3、当一个对象有多个子类时,创建的过程更加麻烦。
使用静态变量的缺陷。
1、静态变量的内存空间是应用程序启动时创建、并在程序消亡时统一释放。
2、在高并发场景下,静态变量容易被不同的线程频繁读写,从而成为系统的主要性能瓶颈。
为啥要使用依赖注入
在.NET Core或.NET FX中目前已经倾向于使用依赖注入框架来对对象的创建过程进行统一管理,这样的好处:
1、封装了对象的创建过程,可以实现对象创建过程和内存管理过程的一致性,
2、如果一个对象有多个实现,也容易使用依赖注入对象对其进行拆分。
3、提供统一的对象生命周期管理。
依赖注入的生命周期
目前在.NET Core(.NET FX也有)中提供了3种依赖注入的生命周期,分别是:
1、单例的生命周期SingleTon。每次从同根容器中(同根 IServiceProvider)获取的时候都是同一个实例,类似于静态变量。主要用来存储系统中需要保持唯一一份的对象
2、瞬时的生命周期Transient。每次从容器 (IServiceProvider)中获取的时候都是一个新的实例,可以用于 控制台程序。
3、作用域的生命周期Scoped。每次从同一个容器中获取的实例是相同的。往往用于ASP.NET Core 网站。
使用依赖注入的方式实现对象的创建虽然会给程序带来一点不便利,但是容易实现程序的耦合度降低,更有利于程序未来的横向扩展,也逐渐成为ASP.NET开发(不仅仅是.NET Core)的的一种最佳实践。
依赖注入的使用
目前.NET中的依赖注入组件是基于.NET Strandard(标准库)进行开发的无其他依赖项的组件,能够被基于标准库的各种.NET技术方案所实现。
主要分为两个步骤,
1:初始化时,将对象注册到依赖注入组件中;例如:
1 | serviceCollection.AddScoped<DBContext>(); |
2:对象使用时,从依赖注入组件中取出对象。
1 | var dbContext = MyServiceProvider.GetService<DBContext>(); |
从依赖注入组件中取出对象的过程,往往会伴随着对象创建的过程,即在依赖注入框架内部,封装了原来与 New 一致的流程。
如果一个对象如果需要引用的对象已经被注册到依赖注入组件中,则无需使用 GetService 方法从依赖注入组件中取出对象,依赖注入框架在创建对象时,也会连带着将其他与之相关的对象一起创建。
示例
例如:
1、在示例代码中,设置 DBContext 为 Scoped
1 | serviceCollection.AddScoped<DBContext>(); |
1 | static ServiceProvider MyServiceProvider; |
那么在创建 UserDbService 对象时,依赖注入框架会自动创建dbContext对象,并将其注入到 UserDbService中。
1 | public UserDbService(DBContext dBContext) |
这样就封装了原来的
1 | IUserDBService dbService=new UserDBService(new DBContext()); |
的代码过程,弥补了调用者需要与UserDBService 高度耦合的缺陷。
值得注意的是:
1、在.NET Core(.NET Framework) 控制台中,需要手动创建对象IServiceCollection,并创建一个静态的 ServiceProvider 对象,由这个对象来实现对象的加载。
2、在.NET Core 网站项目中,由WebHost管理的生命周期,会创建一个统一的 IServiceCollection和 ServiceProvider 对象,无需再单独创建这个ServiceProvider.
示例代码
在示例代码中,提供了一个DIExtension的静态类,并定义了三个注册对象的方法。
1 | public static IServiceCollection UseDBContext(this IServiceCollection serviceCollection) |